/**
 * Procesadores de Lenguaje, curso 2009-2010
 * @author grupo 02
 *
 */
package traductor;

import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Hashtable;
import traductor.AnalizadorLexico.CatLexica;
import traductor.AtributosSintact.clases;
import traductor.Tipos.tiposPerm;
import util.Instruction;

public class AnalizadorSintactico {

    private AnalizadorLexico anLex; //instancia del analizador lexico
    private Hashtable ts; //tabla de simbolos del analizador sintactico
    private int dir; //direccion actual de la tabla de simbolos
    private ArrayList<String> err; //lista de errores
    private ArrayList<String> cod; //atributo para ir acumulando el codigo de la maquinaP
    private ArrayList<Instruction> codByte; //atributo par ir acumulando el codigo de la maquinaP en formato Bytecode
    private String codDebug; //atributo para ir acumulando el codigo de la maquinaP en formato texto
    private PrintWriter archivoCodigo; //atributo para generar el archivo de codigo de maquina P
    private ObjectOutputStream archivoCodigoBytecode; //stream donde generar el bytecode de maquina P
    private Token token; //token actual que estamos procesando
    private Hashtable palRes; //tabla de palabras reservadas
    private int etq; //para el etiquetado de instrucciones máquina P
    private int etqParcheo;
    private int nivel; //nivel de anidamiento
    private int nivelMax; //nivel máximo de anidamiento
    private int tamLocales;
    private ArrayList pend; //pendientes de declarar
    private int tamPl = 0;

    /*Constructor que recibira la instancia  del analizador lexico y la ruta del
    fichero destino del lenguaje objeto*/
    public AnalizadorSintactico(AnalizadorLexico lex, String ficheroDestino) throws Exception {
        this.anLex = lex;
        this.archivoCodigo = new PrintWriter(ficheroDestino + ".txt");
        this.archivoCodigoBytecode = new ObjectOutputStream(new FileOutputStream(ficheroDestino));
        this.err = new ArrayList<String>();
        this.cod = new ArrayList<String>();
        this.codByte = new ArrayList<Instruction>();
        this.codDebug = "";

        this.pend = new ArrayList();
        token = anLex.sigToken(); //cogemos el primer token
        rellenarTablaPalRes(); //rellenamos nuestra tabla de palabras reservadas
    }

    /*Metodo que inicia el analisis sintactico*/
    public void analisisSintactico() throws Exception {
        Prog();
    }

    /*Metodos que implementan la traduccion predictivo-recursivo*/
    private void Prog() throws Exception {
        this.nivel = 0;
        this.ts = new Hashtable();
        this.etq = 1;
        inicioMaqP();

        emite("ir_a #");
        emiteBC(Instruction.code_IR_A);

        etqParcheo = etq-1;
        AtributosSintact partedecs = ParteDecs(dir,false);
        tamLocales = partedecs.getTamLocal();
        parcheaConfig();
        parcheaInicio();
        
        ckToken(CatLexica.SEP);
        Ins();

        if (!pend.isEmpty()) {
            err.add("Error global, hay cosas pendientes por declarar:");
            for (int i = 0; i < pend.size();i++){
                String variable = (String)pend.get(i);
                err.add("varible no declarada: " + variable);
            }
        }        
        emite("stop");
        emiteBC(Instruction.code_STOP);
        codDebug += "stop\n";
        generarArchivo();
        archivoCodigo.close();
        archivoCodigoBytecode.close();
        System.out.println("Codigo generado de la maquina p:");
        System.out.println(cod);
        System.out.println("Codigo generado en texto: ");
        System.out.println(codDebug);

    }

    private AtributosSintact ParteDecs(int dirInicio, boolean proc) throws Exception {
        int dirAnt = dir;
        dir = dirInicio;
        AtributosSintact pdec = new AtributosSintact();
        //Si nos encontramos un separador, no hay parte de declaraciones
        if (token.getCat() != CatLexica.SEP) {
            pdec = Decs();
        }
        if (proc)
            dir = dirAnt;
        return pdec;
    }

    private AtributosSintact Decs() throws Exception {
        AtributosSintact dec = Dec();
        if (existeID(dec.getId())) {
            this.err.add("Linea "+ (token.getNumLinea() - 1)  + " , Columna " + token.getNumCol() +  ": Variable " + dec.getId() + " ya declarada anteriormente");
        }
        else{
            if (dec.getClase()!=clases.PROC){
                dec.setDir(dir);
            }
            ponID(dec.getId(),dec);
            if (dec.getClase()!=clases.TIPO && dec.getClase()!=clases.PROC) {
                dir = dir + dec.getTipo().getTam();
                dec.setTamLocal(dec.getTipo().getTam());
            }
            if (dec.getClase() == clases.TIPO){
                pend.remove(dec.getId());
            }
        }
        return RD(dec);
        //return dec;
    }

    private AtributosSintact RD(AtributosSintact atrAux) throws Exception {
        //Si nos viene un punto y coma, habrá más declaraciones.
        if (token.getCat() == CatLexica.PYCOMA) {
            ckToken(CatLexica.PYCOMA);
            AtributosSintact dec = Dec();
            
            if (existeID(dec.getId())) {
                this.err.add("Linea "+ (token.getNumLinea() - 1)  + " , Columna " + token.getNumCol() +  ": Variable " + dec.getId() + " ya declarada anteriormente");
            }
            else {
                if (dec.getClase()!=clases.PROC){
                    dec.setDir(dir);
                }
                ponID(dec.getId(),dec);
                if (dec.getClase()!=clases.TIPO && dec.getClase()!=clases.PROC) {
                    dir = dir + dec.getTipo().getTam();
                    dec.setTamLocal(atrAux.getTamLocal()+dec.getTipo().getTam());
                }
                if (dec.getClase() == clases.TIPO){
                    pend.remove(dec.getId());
                }
            }
            return RD(dec);
        }
        else return atrAux;
    }

    private AtributosSintact Dec() throws Exception {
        if (token.getCat() == CatLexica.IDENT) {
            AtributosSintact decvar = DecVar();
            decvar.setClase(clases.VAR);
            decvar.setN(nivel);
            return decvar;
        } else if (token.getCat() == CatLexica.TIPO) {
            AtributosSintact dectipo = DecTipo();
            dectipo.setClase(clases.TIPO);
            dectipo.setN(nivel);
            return dectipo;
        } else if (token.getCat() == CatLexica.PROC) {
            AtributosSintact decproc = DecProc();
            decproc.setClase(clases.PROC);
            return decproc;
        }
        throw new Exception("Linea " + token.getNumLinea() + " , Columna " + token.getNumCol() + ": Error de sintaxis, token '"+ token.getLex() +"' no esperado\n");
    }

    private AtributosSintact DecVar() throws Exception {
        AtributosSintact dec = new AtributosSintact();
        if (esPalabraReservada(token.getLex())) {
            err.add("Linea " + (token.getNumLinea()) + " , Columna " + token.getNumCol() + ": Identificador '" + token.getLex() + "' no valido, no se pueden usar palabras reservadas");
            token = this.anLex.sigToken();
            ckToken(CatLexica.DOS_PUNTOS);
            dec.getTipo().setT(tiposPerm.ERR);
            token = this.anLex.sigToken();
        } else {
            dec.setId(token.getLex());
            ckToken(CatLexica.IDENT);
            ckToken(CatLexica.DOS_PUNTOS);
            AtributosSintact tipo = Tipo(false);
            dec.setTamLocal(tipo.getTipo().getTam());
            dec.setTipo(tipo.getTipo());
        }
        return dec;
    }

    private AtributosSintact DecTipo() throws Exception {
        ckToken(CatLexica.TIPO);
        AtributosSintact dectipo = new AtributosSintact();
        String idlex = token.getLex();
        dectipo.setId(idlex);
        ckToken(CatLexica.IDENT);
        ckToken(CatLexica.IGUAL);
        AtributosSintact tipo = Tipo(false);
        dectipo.setTipo(tipo.getTipo());
        return dectipo;
    }

    private AtributosSintact DecProc() throws Exception {
        ckToken(CatLexica.PROC);
        AtributosSintact decproc = new AtributosSintact();
        String idlex = token.getLex();
        nivel++;
        nivelMax++;
        ckToken(CatLexica.IDENT);
        AtributosSintact aux = (AtributosSintact) ts.get(idlex);
        if (existeID(idlex)&&(aux.getN()==nivel)) {
            err.add("Linea " + (token.getNumLinea()) + " , Columna " + token.getNumCol() + ": Variable ya declarada en este nivel");
        }
        Hashtable ts_main = (Hashtable) ts.clone();
        AtributosSintact fparams = FParams();

        fparams.setN(nivel);
        ponID(idlex, fparams);
        AtributosSintact bloqueproc = BloqueProc(fparams);

        decproc.setClase(clases.PROC);
        decproc.setTipo(new Tipos(tiposPerm.PROC));
        decproc.setId(idlex);
        decproc.setN(nivel);
        decproc.setParameters(fparams.getParameters());
        decproc.setInicio(bloqueproc.getInicio());

        ts = ts_main;
        nivel--;
        return decproc;
    }

    private AtributosSintact FParams() throws Exception {
        AtributosSintact fparams = new AtributosSintact();
        if (token.getCat() == CatLexica.PAP){
            Hashtable ts_main = (Hashtable) ts.clone();
            ckToken(CatLexica.PAP);
            AtributosSintact lfparams = LFParams();
            ckToken(CatLexica.PCIE);
            ArrayList<Params> params1 = lfparams.getParameters();
            fparams.setParameters(params1);
            fparams.setDir(lfparams.getDir());
            fparams.setTamLocal(lfparams.getTamLocal());
        } else {           
            ArrayList parameters = new ArrayList<Params>();
            fparams.setParameters(parameters);
            fparams.setDir(0);
            fparams.setTamLocal(0);
        }
        return fparams;
    }

    private AtributosSintact LFParams() throws Exception {
        AtributosSintact lfparams = new AtributosSintact();

        int dirh = 0;

        AtributosSintact fparam = FParam(dirh);

        String id1 = fparam.getId();
        fparam.setN(nivel);
        ponID(id1, fparam);

        ArrayList<Params> params1 = fparam.getParameters();
        ArrayList<Params> paramsh = new ArrayList<Params>();
        paramsh.add(params1.get(0));

        int dirh2 = fparam.getTipo().getTam();
        tamPl = tamPl+fparam.getTipo().getTam();
        lfparams.setTamLocal(lfparams.getTamLocal()+fparam.getTipo().getTam());
        AtributosSintact rf = RF(paramsh, dirh2);

        lfparams.setParameters(rf.getParameters());
        lfparams.setDir(rf.getDir());

        return lfparams;
    }

    private AtributosSintact RF(ArrayList<Params> paramsh, int dirh) throws Exception{
        AtributosSintact rf = new AtributosSintact();
        if (token.getCat() == CatLexica.COMA){
            ckToken(CatLexica.COMA);
            int dirh1 = dirh;

            AtributosSintact fparam = FParam(dirh1);
            String id1 = fparam.getId();
            ArrayList<Params> params1 = fparam.getParameters();

            paramsh.add(params1.get(0));
            if(existeID(id1)){
                    AtributosSintact aux = (AtributosSintact) ts.get(id1);
                    if (aux.getN()==nivel){
                        err.add("Linea " + (token.getNumLinea()) + " , Columna " + token.getNumCol() + ": Variable ya declarada en este nivel");
                    }
            }
            fparam.setDir(dirh);
            fparam.setN(nivel);
            ponID(id1, fparam);
            int dirh2 = dirh + fparam.getTipo().getTam();
            tamPl = tamPl+fparam.getTipo().getTam();
            rf.setTamLocal(rf.getTamLocal()+fparam.getTipo().getTam());
            AtributosSintact rf2 = RF(paramsh, dirh2);

            
            rf.setParameters(rf2.getParameters());
            rf.setDir(rf2.getDir());            

        } else {
            rf.setDir(dirh);
            rf.setParameters(paramsh);
        }
        return rf;
    }

    private AtributosSintact FParam(int dirh) throws Exception {
        AtributosSintact fparam = new AtributosSintact();
        if (token.getCat() == CatLexica.VAR){
            ckToken(CatLexica.VAR);
            String id = token.getLex();
            ckToken(CatLexica.IDENT);
            ckToken(CatLexica.DOS_PUNTOS);
            AtributosSintact tipo = Tipo(false);
            
            fparam.setId(id);
            fparam.setTipo(tipo.getTipo());
            fparam.setN(tipo.getN());
            fparam.getTipo().setTam(1);
            fparam.setClase(clases.PVAR);
            fparam.setDir(dirh);

            Params params = new Params(Params.modos.VAR,tipo.getTipo(),dirh);
            fparam.addParam(params);
            fparam.setTamLocal(fparam.getTamLocal()+tipo.getTipo().getTam());
        }
        else if (token.getCat() == CatLexica.IDENT){
            String id = token.getLex();
            ckToken(CatLexica.IDENT);
            ckToken(CatLexica.DOS_PUNTOS);
            AtributosSintact tipo = Tipo(false);

            fparam.setId(id);
            fparam.setTipo(tipo.getTipo());
            fparam.setN(tipo.getN());
            fparam.getTipo().setTam(tipo.getTipo().getTam());
            fparam.setClase(clases.VAR);
            fparam.setDir(dirh);

            Params params = new Params(Params.modos.VALOR,tipo.getTipo(),dirh);
            fparam.addParam(params);
            fparam.setTamLocal(fparam.getTamLocal()+tipo.getTipo().getTam());
        }
        return fparam;
    }

    private AtributosSintact BloqueProc(AtributosSintact a) throws Exception {
        AtributosSintact bloqueproc = new AtributosSintact();
        if (token.getCat() == CatLexica.FORWARD) {
            CuerpoDiferido(a);
        } else {
            bloqueproc = CuerpoDefinido(a);
        }
        return bloqueproc;
    }

    private void CuerpoDiferido(AtributosSintact atr) throws Exception {
        ckToken(CatLexica.FORWARD);
        throw new Exception("Linea " + token.getNumLinea() + " , Columna " + token.getNumCol() + ": Token no esperado\n");
    }

    private AtributosSintact CuerpoDefinido(AtributosSintact atr) throws Exception {
        AtributosSintact cuerpodefinido = new AtributosSintact();
        if (token.getCat() == CatLexica.LL_AP) ckToken(CatLexica.LL_AP);
        AtributosSintact aux = ParteDecs(atr.getDir(),true);
        int tamLocal = aux.getTamLocal()+atr.getTamLocal();
        int dir1 = tamLocal;
        int niv = atr.getN();
        int inicio = etq;
        Prologo(niv,dir1);
        ckToken(CatLexica.SEP);
        Ins();
        if (token.getCat() == CatLexica.LL_CIE) ckToken(CatLexica.LL_CIE);
        Epilogo(niv);
        emite("ir_ind()");
        emiteBC(Instruction.code_IR_IND);
        cuerpodefinido.setInicio(inicio);
        return cuerpodefinido;
    }

    private void Prologo(int nivel,int direccion) throws Exception {
        emite("apila-dir(0)");
        emiteBC(Instruction.code_APILA_DIR,"0");
        emite("apila(2)");
        emiteBC(Instruction.code_APILA,"2");
        emite("suma");
        emiteBC(Instruction.code_SUMA);
        emite("apila-dir("+(1+nivel)+")");
        emiteBC(Instruction.code_APILA_DIR,String.valueOf(1+nivel));
        emite("desapila-ind"); //display salvado
        emiteBC(Instruction.code_DESAPILA_IND);
        emite("apila-dir(0)");
        emiteBC(Instruction.code_APILA_DIR,"0");
        emite("apila(3)");
        emiteBC(Instruction.code_APILA,"3");
        emite("suma");
        emiteBC(Instruction.code_SUMA);
        emite("desapila-dir("+(1+nivel)+")"); //valor de display actual fijado
        emiteBC(Instruction.code_DESAPILA_DIR,String.valueOf(1+nivel));
        emite("apila-dir(0)");
        emiteBC(Instruction.code_APILA_DIR,"0");
        emite("apila("+(direccion+2)+")");
        emiteBC(Instruction.code_APILA,String.valueOf(direccion+2));
        emite("suma");
        emiteBC(Instruction.code_SUMA);
        emite("desapila-dir(0)"); //espacio reservado
        emiteBC(Instruction.code_DESAPILA_DIR,"0");
    }

    private void Epilogo(int nivel) throws Exception {
        emite("apila-dir("+(1+nivel)+")");
        emiteBC(Instruction.code_APILA_DIR,String.valueOf(1+nivel));
        emite("apila(2)");
        emiteBC(Instruction.code_APILA,"2");
        emite("resta");
        emiteBC(Instruction.code_RESTA);
        emite("apila-ind"); //apilada dirección retorno
        emiteBC(Instruction.code_APILA_IND);
        emite("apila-dir("+(1+nivel)+")");
        emiteBC(Instruction.code_APILA_DIR,String.valueOf(1+nivel));
        emite("apila(3)");
        emiteBC(Instruction.code_APILA,"3");
        emite("resta");
        emiteBC(Instruction.code_RESTA);
        emite("copia");
        emiteBC(Instruction.code_COPIA);
        emite("desapila-dir(0)"); //fijado cp
        emiteBC(Instruction.code_DESAPILA_DIR,"0");
        emite("apila(2)");
        emiteBC(Instruction.code_APILA,"2");
        emite("suma");
        emiteBC(Instruction.code_SUMA);
        emite("apila-ind");
        emiteBC(Instruction.code_APILA_IND);
        emite("desapila-dir("+(1+nivel)+")"); //recuperado antiguo display
        emiteBC(Instruction.code_DESAPILA_DIR,String.valueOf(1+nivel));
        
    }

    private void IComp() throws Exception {
        if (token.getCat() == CatLexica.LL_AP) {
            ckToken(CatLexica.LL_AP);
            Ins();
            ckToken(CatLexica.LL_CIE);
        } else {
            I();
        }
    }

    private void Ins() throws Exception {
        I();
        RI();
    }

    private void I() throws Exception {
        switch (token.getCat()) {
            case IDENT: //Diferenciacion entre asignacion y llamada a procedimiento
                AtributosSintact aux = (AtributosSintact)ts.get(token.getLex());
                if (aux == null)
                    throw new Exception("Linea " + token.getNumLinea() + " , Columna " + token.getNumCol() + ": Identificador no declarado\n");
                else if (aux.getClase() == clases.PROC)
                    IProc();
                else
                    IAsig();
                break;
            case IN:
                ILect();
                break;
            case OUT:
                IEsc();
                break;
            case IF:
                IIf();
                break;
            case WHILE:
                IWhile();
                break;
            case FOR:
                IFor();
                break;
            case NEW:
                INew();
                break;
            case DISPOSE:
                IDispose();
                break;
            default:
                throw new Exception("Linea " + token.getNumLinea() + " , Columna " + token.getNumCol() + ": Error de sintaxis, token no esperado\n");
        }
    }

    private void RI() throws Exception {
        if (token.getCat() == CatLexica.PYCOMA) {
            ckToken(CatLexica.PYCOMA);
            I();
            RI();
        }
    }

    private AtributosSintact ILect() throws Exception {
        AtributosSintact iLect = new AtributosSintact();
        ckToken(CatLexica.IN);
        ckToken(CatLexica.PAP);
        AtributosSintact exp = Exp0(false,true);
        if (exp.getTipo().getT() == tiposPerm.ERR) {
            err.add("Linea " + token.getNumLinea() + " , Columna " + token.getNumCol() + ": Expresion erronea");
        }
        ckToken(CatLexica.PCIE);
        emite("leer");
        emiteBC(Instruction.code_LEER); // "0111 0100" leer
        codDebug += "leer\n";
        emite("desapila_ind ");
        emiteBC(Instruction.code_DESAPILA_IND);
        codDebug += "desapila_ind\n";
        return iLect;
    }

    private AtributosSintact IEsc() throws Exception {
        AtributosSintact iEsc = new AtributosSintact();
        ckToken(CatLexica.OUT);
        ckToken(CatLexica.PAP);
        AtributosSintact exp = Exp0(false,false);
        if (exp.getTipo().getT() == tiposPerm.ERR) {
            err.add("Linea " + token.getNumLinea() + " , Columna " + token.getNumCol() + ": Expresion erronea");
        }
        ckToken(CatLexica.PCIE);
        emite("escribir");
        emiteBC(Instruction.code_ESCR); // "0111 0101" escribir
        codDebug += "escribir\n";
        return iEsc;
    }

    private AtributosSintact IAsig() throws Exception {
        AtributosSintact iasig = new AtributosSintact();
        AtributosSintact mem = Mem();
        ckToken(CatLexica.ASIG);
        AtributosSintact exp0 = Exp0(false,false);
        String nul = exp0.getTipo().getIdt();
        iasig.setTipo(exp0.getTipo());
        iasig.setId(mem.getId());
        iasig.setDir(mem.getDir());
        iasig.setN(mem.getN());
       
        AtributosSintact num = new AtributosSintact();
        num.getTipo().setT(tiposPerm.NATURAL);
        AtributosSintact bool = new AtributosSintact();
        bool.getTipo().setT(tiposPerm.BOOLEAN);
        //cod←if(tiposCompatibles(tipo1, <t: num>, ts)∨ tiposCompatibles(tipo1, <t:bool>, ts), emite(desapila_ind), mueve(tipo1.tam));}
        if (tiposCompatibles(mem, num) || tiposCompatibles(mem, bool) || nul.equals("null")) {
            emite("desapila_ind");
            emiteBC(Instruction.code_DESAPILA_IND);
        }
        else {
            emite("mueve("+mem.getTipo().getTam()+")");
            emiteBC(Instruction.code_MUEVE);
        }
        return iasig;
    }

    private AtributosSintact IIf() throws Exception {
        ckToken(CatLexica.IF);
        AtributosSintact iif = new AtributosSintact();
        AtributosSintact exp0 = Exp0(false,false);
        if (exp0.getTipo().getT()!=tiposPerm.BOOLEAN){
            err.add("Linea " + token.getNumLinea() + " , Columna " + token.getNumCol() + ": Tipo de expresión debe ser booleana");
        }
        ckToken(CatLexica.THEN);
        int posMarca1 = cod.size();
        emite("ir_f #");
        emiteBC(Instruction.code_IR_F);
        codDebug += "ir_f #\n";
        IComp();
        int etqElse = etq;
        int posMarca2 = cod.size();
        if (token.getCat() == CatLexica.ELSE) {
            emite("ir_a $");
            emiteBC(Instruction.code_IR_A);
            codDebug += "ir_a $\n";
            PElse();
            etqElse++; //Si hay parte else, tenemos que sumar 1 para ponernos delante del ir_a
        }
        int etqFinElse = etq;
        parchea(posMarca1, posMarca2, etqElse, etqFinElse);
        return iif;
    }

    private void PElse() throws Exception {
        ckToken(CatLexica.ELSE);
        IComp();
    }

    private AtributosSintact IWhile() throws Exception {
        ckToken(CatLexica.WHILE);
        int etqWhile = etq; //guardamos la etq de retorno de salto
        AtributosSintact exp0 = Exp0(false,false);
        if (exp0.getTipo().getT()!=tiposPerm.BOOLEAN){
            err.add("Linea " + token.getNumLinea() + " , Columna " + token.getNumCol() + ": Tipo de expresión debe ser booleana");
        }
        int posMarca = cod.size(); //guardamos la posición del arraylist cod
        emite("ir_f #");
        emiteBC(Instruction.code_IR_F);
        codDebug += "ir_f #\n";
        ckToken(CatLexica.DO);
        IComp();
        emite("ir_a " + etqWhile);
        emiteBC(Instruction.code_IR_A,String.valueOf(etqWhile));
        codDebug += "ir_a " + etqWhile + '\n';
        int etqFinWhile = etq;
        parchea(posMarca, etqFinWhile);
        return new AtributosSintact();
    }

    private AtributosSintact IFor() throws Exception {
        ckToken(CatLexica.FOR);
        
        AtributosSintact iasig = IAsig();
        int dirVar = iasig.getDir();
        int nivVar = iasig.getN()+1;
        int etqFor = etq; //guardamos la etq de retorno de salto

        //apilamos la variable
        emite("apila_dir("+nivVar+")");
        emiteBC(Instruction.code_APILA_DIR, Integer.toString(nivVar));
        emite("apila("+ dirVar + ")");
        emiteBC(Instruction.code_APILA, Integer.toString(dirVar));
        emite("suma");
        emiteBC(Instruction.code_SUMA);
        emite("apila_ind");
        emiteBC(Instruction.code_APILA_IND);

        ckToken(CatLexica.TO);        
        AtributosSintact exp1 = Exp1(false,false);
        if((iasig.getTipo().getT() != tiposPerm.INTEGER)&&(iasig.getTipo().getT() != tiposPerm.NATURAL)){
            err.add("Linea " + token.getNumLinea() + " , Columna " + token.getNumCol() + ": Tipo de expresión debe ser natural o entera");
        }
        if((exp1.getTipo().getT() != tiposPerm.INTEGER)&&(exp1.getTipo().getT() != tiposPerm.NATURAL)){
            err.add("Linea " + token.getNumLinea() + " , Columna " + token.getNumCol() + ": Tipo de expresión debe ser natural o entera");
        }
        //Comprobación de tipos compatibles entre ident y exp0 no hecho al estar ya hecho en Iasig
        if(!tiposCompatibles(iasig, exp1)){
            err.add("Linea " + token.getNumLinea() + " , Columna " + token.getNumCol() + ": Tipo de identificador y expresión de fin incompatibles");
        }        
        
        emite("menor"); //cuando ya no sea menor o igual, da false y hace ir_f
        emiteBC(Instruction.code_MENOR);
        int posMarca = cod.size(); //guardamos la posición del arraylist cod
        emite("ir_f #");
        emiteBC(Instruction.code_IR_F);
        codDebug += "ir_f #\n";
        ckToken(CatLexica.DO);
        IComp();

        //Apilamos la dirección de la variable a incrementar
        emite("apila_dir("+nivVar+")");
        emiteBC(Instruction.code_APILA_DIR,Integer.toString(nivVar));
        emite("apila("+dirVar+")");
        emiteBC(Instruction.code_APILA,Integer.toString(dirVar));
        emite("suma");
        emiteBC(Instruction.code_SUMA);

        //Apilamos la variable a incrementar
        emite("apila_dir("+nivVar+")");
        emiteBC(Instruction.code_APILA_DIR, Integer.toString(nivVar));
        emite("apila("+ dirVar + ")");
        emiteBC(Instruction.code_APILA, Integer.toString(dirVar));
        emite("suma");
        emiteBC(Instruction.code_SUMA);
        emite("apila_ind");
        emiteBC(Instruction.code_APILA_IND);

        //para incrementar el indice del for
        emite("apila(1)"); 
        emiteBC(Instruction.code_APILA, Integer.toString(1));

        //Lo guardamos en la dirección previamente apilada
        emite("suma");  //+1 al índice del for
        emiteBC(Instruction.code_SUMA);
        emite("desapila_ind"); //para machacarlo con el valor actual del índice
        emiteBC(Instruction.code_DESAPILA_IND);

        //Volvemos a comprobar la condición del bucle
        emite("ir_a " + etqFor);
        emiteBC(Instruction.code_IR_A,String.valueOf(etqFor));
        codDebug += "ir_a " + etqFor + '\n';
        int etqFinFor = etq;
        parchea(posMarca, etqFinFor);
        return new AtributosSintact();
    }

    private void inicioMaqP() {
        //Instrucciones iniciales, preparacion maquinaP. Se parchea el valor emitido
        emite("apila #");
        emiteBC(Instruction.code_APILA,"#");
        emite("desapila_dir (1)");
        emiteBC(Instruction.code_DESAPILA_DIR,"1");
        emite("apila $");
        emiteBC(Instruction.code_APILA,"$");
        emite("desapila_dir (0)");
        emiteBC(Instruction.code_DESAPILA_DIR,"0");
        emite("seg $");
        emiteBC(Instruction.code_SEG,"$");
    }

    //parchea para IProc
    private void parcheaIProc(int marca, int etqRetorno){
        int etqAux = marca + 1;
        String aux = new String(etqAux + " apila " + etqRetorno + '\n');
        cod.set(marca, aux);
        
        //parcheo del bytecode:
        codByte.set(marca, new Instruction(marca, (byte) Instruction.code_APILA, Integer.toString(etqRetorno)));

    }

    //parchea para Whiles
    private void parchea(int marca, int etqFinWhile) {
        int etqAux = marca + 1;
        String aux = new String(etqAux + " ir_f " + etqFinWhile + '\n');
        cod.set(marca, aux);

        //parcheo del bytecode:
        codByte.set(marca, new Instruction(etqAux, (byte) Instruction.code_IR_F, Integer.toString(etqFinWhile)));
    }

    //parchea para Ifs
    private void parchea(int marca1, int marca2, int etqElse, int etqFinElse) throws Exception {
        int etqAux;
        if (etqElse != etqFinElse) { //Hay parte else
            etqAux = marca2 + 1;
            String auxElse = new String(etqAux + " ir_a " + etqFinElse + '\n');
            cod.set(marca2, auxElse);
            
            //parcheo del bytecode:
            codByte.set(marca2, new Instruction(etqAux, (byte) Instruction.code_IR_A, Integer.toString(etqFinElse)));
        }
        etqAux = marca1 + 1;
        String auxFinElse = new String(etqAux + " ir_f " + etqElse + '\n');
        cod.set(marca1, auxFinElse);
        
        //parcheo del bytecode:
        codByte.set(marca1, new Instruction(etqAux, (byte) Instruction.code_IR_F, Integer.toString(etqElse)));
    }

    private void parcheaConfig(){

        String aux = new String("1 "+ "apila("+ (nivelMax+2)+")\n");
        cod.set(0, aux);
        codByte.set(0, new Instruction(1,(byte) Instruction.code_APILA, Integer.toString(nivelMax+2)));

        String aux2 = new String("3 "+ "apila("+ (1+nivelMax+tamLocales)+")\n");
        cod.set(2, aux2);
        codByte.set(2, new Instruction(3,(byte) Instruction.code_APILA, Integer.toString(1+nivelMax+tamLocales)));

        String aux3 = new String("5 "+ "seg("+ (1+1+nivelMax+tamLocales)+")\n");
        cod.set(4, aux3);
        codByte.set(4, new Instruction(5,(byte) Instruction.code_SEG, Integer.toString(2+nivelMax+tamLocales)));
    }

    private void parcheaInicio() {
        String aux = new String(etqParcheo + " ir_a " + etq + '\n');
        cod.set((etqParcheo -1), aux);
        codByte.set(etqParcheo -1, new Instruction(etqParcheo,(byte)Instruction.code_IR_A,Integer.toString(etq)));

        codByte.set((etqParcheo-1), new Instruction(etqParcheo, (byte) Instruction.code_IR_A, Integer.toString(etq)));       
    }

    private void AParams(AtributosSintact f) throws Exception {
        //f es el atributo sintactico que contiene la lista de parametros
        if (token.getCat() == CatLexica.PAP) { //AParams --> PAP LAParams PCIE
            ckToken(CatLexica.PAP);
            //Estas instrucciones equivalen a: emite("inicio_paso");
            emite("apila-dir 0");
            emiteBC(Instruction.code_APILA_DIR, "0");
            emite("apila 3");
            emiteBC(Instruction.code_APILA, "3");
            emite("suma");
            emiteBC(Instruction.code_SUMA);

            int nparams = LAParams(f).size();
            ckToken(CatLexica.PCIE);
            if (nparams != f.getParameters().size())
                err.add("Linea " + token.getNumLinea() + " , Columna " + token.getNumCol() + ": Número de parámetros erróneo en la llamada al procedimiento");
            
            //Esta instruccion equivale a: emite("fin_paso");
            emite("desapila");
            emiteBC(Instruction.code_DESAPILA);
        }
        else //AParams --> lambda
            if (f.getParameters().size() > 0)
                err.add("Linea " + token.getNumLinea() + " , Columna " + token.getNumCol() + ": Número de parámetros erróneo en la llamada al procedimiento");
    }

    private ArrayList<Params> LAParams(AtributosSintact f) throws Exception {
        //f es el atributo sintactico que contiene la lista de parametros
        emite("copia");
        emiteBC(Instruction.code_COPIA);
        AtributosSintact exp0 = Exp0(f.getParameters().get(0).getModo()==Params.modos.VAR,false);
        int nparamsh = 0;
        emite("flip");
        emiteBC(Instruction.code_FLIP);

        pasoParametro(exp0.getModo(), f.getParameters().get(0));  
        ArrayList fparams1 = RA(nparamsh, f);
        if (fparams1.size() == 0 || !tiposCompatibles(f.getParameters().get(0), exp0))
            err.add("Linea " + token.getNumLinea() + " , Columna " + token.getNumCol() + ": Parámetros erróneos en la llamada al procedimiento");
        return fparams1;
    }

    private ArrayList<Params> RA(int nparams, AtributosSintact f) throws Exception {
        //f es el atributo sintactico que contiene la lista de parametros
        if (token.getCat() == CatLexica.COMA) { //RA --> coma Exp0 RA
            ckToken(CatLexica.COMA);
            emite("copia");
            emiteBC(Instruction.code_COPIA);
            nparams++;
            AtributosSintact exp0 = Exp0(f.getParameters().get(nparams).getModo()==Params.modos.VAR,false);
            emite("flip");
            emiteBC(Instruction.code_FLIP);
            
            pasoParametro(exp0.getModo(), f.getParameters().get(nparams));
            ArrayList fparams1 = RA(nparams, f);
            if (nparams > fparams1.size() - 1 || !tiposCompatibles(f.getParameters().get(nparams), exp0))
                err.add("Linea " + token.getNumLinea() + " , Columna " + token.getNumCol() + ": Parámetros erróneos en la llamada al procedimiento");
            return fparams1;
        }
        else //RA --> lambda
            return f.getParameters();
    }

    private void pasoParametro(Params.modos modoReal, Params pformal) {
        emite("apila " + pformal.getDir());
        emiteBC(Instruction.code_APILA, String.valueOf(pformal.getDir()));
        emite("suma");
        emiteBC(Instruction.code_SUMA);
        emite("flip");
        emiteBC(Instruction.code_FLIP);

        if (pformal.getModo() == Params.modos.VALOR && modoReal == Params.modos.VAR) {
            emite("mueve " + pformal.getTipo().tam);
            emiteBC(Instruction.code_MUEVE, String.valueOf(pformal.getTipo().tam));
        }
        else {
            emite("desapila-ind");
            emiteBC(Instruction.code_DESAPILA_IND);
        }
    }

    private AtributosSintact Exp0(boolean parh, boolean lectura) throws Exception {
        AtributosSintact exp0 = new AtributosSintact();
        AtributosSintact exp1 = Exp1(parh, lectura);
        AtributosSintact r0 = R0(exp1, parh, lectura);
        exp0.setTipo(r0.getTipo());
        exp0.setId(exp1.getId());
        if (parh)
            exp0.setModo(Params.modos.VAR);
        else
            exp0.setModo(Params.modos.VALOR);
        return exp0;
    }

    private AtributosSintact R0(AtributosSintact h, boolean parh, boolean lectura) throws Exception {
        AtributosSintact r0 = new AtributosSintact();
        CatLexica c = token.getCat();
        if (c.equals(CatLexica.MENOR) || c.equals(CatLexica.MAYOR) || c.equals(CatLexica.MENOR_IGUAL) || c.equals(CatLexica.MAYOR_IGUAL) || c.equals(CatLexica.IGUAL) || c.equals(CatLexica.DISTINTO)) {
            AtributosSintact op = opNivel0(c);
            token = anLex.sigToken();
            AtributosSintact exp1 = Exp1(parh, lectura);
            tiposPerm tipo1 = exp1.getTipo().getT();
            tiposPerm tipo = tipoOpNivel0(h.getTipo().getT(), tipo1);
            r0.getTipo().setT(tipo);
            emite(op.getCod());
            emiteBC(op.getBC());
            codDebug += op.getCodString() + "\n";
        } else { //Lambda
            r0.setTipo(h.getTipo());
        }
        return r0;
    }

    private AtributosSintact Exp1(boolean parh, boolean lectura) throws Exception {
        AtributosSintact exp1 = new AtributosSintact();
        AtributosSintact exp2 = Exp2(parh, lectura);
        AtributosSintact r1 = R1(exp2, parh, lectura);
        exp1.setTipo(r1.getTipo());
        exp1.setId(exp2.getId());
        return exp1;
    }

    private AtributosSintact R1(AtributosSintact h, boolean parh, boolean lectura) throws Exception {
        AtributosSintact r1 = new AtributosSintact();
        CatLexica c = token.getCat();
        if (c.equals(CatLexica.MAS) || c.equals(CatLexica.MENOS) || c.equals(CatLexica.OR)) {
            AtributosSintact opniv1 = opNivel1(c);
            token = anLex.sigToken();
            AtributosSintact exp2 = Exp2(parh, lectura);
            tiposPerm tipo1 = exp2.getTipo().getT();
            tiposPerm tipo2 = tipoOpNivel1(h.getTipo().getT(), tipo1, opniv1);
            AtributosSintact tip2 = new AtributosSintact();
            tip2.getTipo().setT(tipo2);
            AtributosSintact tipo3 = R1(tip2, parh, lectura);
            r1.setTipo(tipo3.getTipo());
            emite(opniv1.getCod());
            emiteBC(opniv1.getBC());
            codDebug += opniv1.getCodString() + "\n";
        } else { //Lambda
            r1.setTipo(h.getTipo());
        }
        return r1;
    }

    private AtributosSintact Exp2(boolean parh, boolean lectura) throws Exception {
        AtributosSintact exp2 = new AtributosSintact();
        AtributosSintact exp3 = Exp3(parh, lectura);
        AtributosSintact r2 = R2(exp3, parh, lectura);
        exp2.setTipo(r2.getTipo());
        exp2.setId(exp3.getId());
        return exp2;
    }

    private AtributosSintact R2(AtributosSintact h, boolean parh, boolean lectura) throws Exception {
        AtributosSintact r2 = new AtributosSintact();
        CatLexica c = token.getCat();
        if (c.equals(CatLexica.MULT) || c.equals(CatLexica.DIV) || c.equals(CatLexica.MOD) || c.equals(CatLexica.AND)) {
            AtributosSintact opniv2 = opNivel2(c);
            token = anLex.sigToken();
            AtributosSintact exp3 = Exp3(parh, lectura);
            tiposPerm tipo1 = exp3.getTipo().getT();
            tiposPerm tipo2 = tipoOpNivel2(h.getTipo().getT(), tipo1, opniv2);
            AtributosSintact tip2 = new AtributosSintact();
            tip2.getTipo().setT(tipo2);
            AtributosSintact tipo3 = R2(tip2, parh, lectura);
            r2.setTipo(tipo3.getTipo());
            emite(opniv2.getCod());
            emiteBC(opniv2.getBC());
            codDebug += opniv2.getCodString() + "\n";
        } else { //Lambda
            r2.setTipo(h.getTipo());
        }
        return r2;
    }

    private AtributosSintact Exp3(boolean parh, boolean lectura) throws Exception {
        AtributosSintact exp3 = new AtributosSintact();
        AtributosSintact exp4 = Exp4(parh, lectura);
        AtributosSintact r3 = R3(exp4, parh, lectura);
        exp3.setTipo(r3.getTipo());
        exp3.setId(exp4.getId());
        return exp3;
    }

    private AtributosSintact R3(AtributosSintact h, boolean parh, boolean lectura) throws Exception {
        AtributosSintact r3 = new AtributosSintact();
        CatLexica c = token.getCat();
        if (c.equals(CatLexica.DESP_DER) || c.equals(CatLexica.DESP_IZQ)) {
            AtributosSintact opniv3 = opNivel3(c);
            token = anLex.sigToken();
            AtributosSintact exp4 = Exp4(parh, lectura);
            tiposPerm tipo1 = exp4.getTipo().getT();
            tiposPerm tipo = tipoOpNivel3(h.getTipo().getT(), tipo1);
            r3.getTipo().setT(tipo);
            emite(opniv3.getCod());
            emiteBC(opniv3.getBC());
            codDebug += opniv3.getCodString() + "\n";
        } else { //Lambda
            r3.setTipo(h.getTipo());
        }
        return r3;
    }

    private AtributosSintact Exp4(boolean parh, boolean lectura) throws Exception {
        AtributosSintact exp40 = new AtributosSintact();
        CatLexica c = token.getCat();
        if (c == CatLexica.BARRA) {
            ckToken(CatLexica.BARRA);
            AtributosSintact exp41 = Exp4(parh, lectura);
            ckToken(CatLexica.BARRA);
            exp40.setTipo(exp41.getTipo());
            emite("absoluto");
            emiteBC(Instruction.code_ABS);
            codDebug += "absoluto\n";
        } else {
            if (c == CatLexica.NOT || c == CatLexica.MENOS) {
                AtributosSintact op4a = opNivel4a(c);
                token = anLex.sigToken();
                AtributosSintact exp41 = Exp4(parh, lectura);
                exp40.getTipo().setT(tipoOpNivel4a(exp41.getTipo().getT(), op4a));
                emite(op4a.getCod());
                emiteBC(op4a.getBC());
                codDebug += op4a.getCodString() + "\n";
            } else {
                if (c == CatLexica.CONV_NAT || c == CatLexica.CONV_INT ||
                        c == CatLexica.CONV_FLOAT || c == CatLexica.CONV_CHAR) {
                    AtributosSintact op4b = opNivel4b(c);
                    token = anLex.sigToken();
                    AtributosSintact exp5 = Exp5(parh,lectura);
                    exp40.getTipo().setT(tipoOpNivel4b(exp5.getTipo().getT(), op4b));
                    emite(op4b.getCod());
                    emiteBC(op4b.getBC());
                    codDebug += op4b.getCodString() + "\n";
                } else {
                    AtributosSintact exp5 = Exp5(parh,lectura);
                    exp40.setTipo(exp5.getTipo());
                    exp40.setId(exp5.getId());
                }
            }
        }
        return exp40;
    }

    private AtributosSintact Exp5(boolean parh, boolean lectura) throws Exception {
        AtributosSintact exp5 = new AtributosSintact();
        CatLexica c = token.getCat();
        if (c == CatLexica.IDENT) {
            exp5 = Mem();
            String memlex = exp5.getId();
            if (existeID(memlex)) {
                AtributosSintact aux = (AtributosSintact) ts.get(memlex);
                exp5.setTipo(aux.getTipo());
            } else {
                exp5.getTipo().setT(tiposPerm.ERR);
                err.add("Linea " + token.getNumLinea() + " , Columna " + token.getNumCol() + ": Variable '" + token.getLex() + "'inexistente");
            }
            AtributosSintact num = new AtributosSintact();
            num.getTipo().setT(tiposPerm.NATURAL);
            AtributosSintact bool = new AtributosSintact();
            bool.getTipo().setT(tiposPerm.BOOLEAN);
            if (((tiposCompatibles(exp5, num) || tiposCompatibles(exp5, bool)) && !parh)&&!lectura) {
                emite("apila_ind");
                emiteBC(Instruction.code_APILA_IND);
            }
        } else {
            if (c == CatLexica.PAP) {
                ckToken(CatLexica.PAP);
                AtributosSintact exp0 = Exp0(parh, lectura);
                exp5.setTipo(exp0.getTipo());
            } else {
                if (c == CatLexica.IDENT) {
                    String idlex = token.getLex();
                    if (ts.containsKey(idlex)) {
                        AtributosSintact iden = (AtributosSintact) ts.get(idlex);
                        exp5.setTipo(iden.getTipo());
                        emite("apila_dir " + iden.getDir());
                        emiteBC(Instruction.code_APILA_DIR, Integer.toString(iden.getDir()));
                        codDebug += "apila_dir(" + iden.getDir() + ")\n";
                    } else {
                        err.add("Linea " + token.getNumLinea() + " , Columna " + token.getNumCol() + ": Variable '" + token.getLex() + "'inexistente");
                    }
                } else {
                    if (c == CatLexica.TRUE || c == CatLexica.FALSE) {
                        exp5.getTipo().setT(tiposPerm.BOOLEAN);
                        if (c == CatLexica.TRUE) {
                            emite("apila(" + "true)");
                            emiteBC(Instruction.code_APILA, "true");
                            codDebug += "apila(true)\n";
                        } else {
                            emite("apila(" + "false)");
                            emiteBC(Instruction.code_APILA, "false");
                            codDebug += "apila(false)\n";
                        }
                    } else {
                        if (c == CatLexica.NUM_NAT || c == CatLexica.CERO) {
                            exp5.getTipo().setT(tiposPerm.NATURAL);
                            exp5.setId(token.getLex());
                            emite("apila(" + token.getLex()+")");
                            emiteBC(Instruction.code_APILA, token.getLex());
                            codDebug += "apila(" + token.getLex() + ")\n";
                        } else {
                            if (c == CatLexica.NUM_FLOAT) {
                                exp5.getTipo().setT(tiposPerm.FLOAT);
                                exp5.setId(token.getLex());
                                emite("apila(" + token.getLex()+")");
                                emiteBC(Instruction.code_APILA, token.getLex());
                                codDebug += "apila(" + token.getLex() + ")\n";
                            } else {
                                if (c == CatLexica.CTE_CHAR) {
                                    exp5.getTipo().setT(tiposPerm.CHAR);
                                    exp5.setId(token.getLex());
                                    emite("apila(" + token.getLex()+")");
                                    emiteBC(Instruction.code_APILA, token.getLex());
                                    codDebug += "apila(" + token.getLex() + ")\n";
                                } else {
                                    if (c == CatLexica.NULL) {
                                        exp5.getTipo().setT(tiposPerm.POINTER);
                                        exp5.setId(token.getLex());
                                        exp5.getTipo().setIdt("null");
                                        emite("apila(00)"); //El 00 indica null para no confundir con el 0
                                        emiteBC(Instruction.code_APILA, "00"); //El 00 indica null
                                    }
                                    else err.add("Linea " + token.getNumLinea() + " , Columna " + token.getNumCol() + ": Expresion erronea");
                                }
                            }
                        }
                    }
                }
            }
            if (token.getCat() != CatLexica.PCIE) {
                token = anLex.sigToken();
            } else {
                ckToken(CatLexica.PCIE);
            }
        }
        return exp5;
    }

    private boolean tiposCompatibles(Params p, AtributosSintact t) {
        ArrayList visitadas = new ArrayList();
        String id1 = p.getTipo().getIdt();
        String id2 = t.getId();
        return tiposCompatibles2(p.getTipo(),t.getTipo(),visitadas,id1,id2);
    }

    private boolean tiposCompatibles(AtributosSintact t1, AtributosSintact t2) {
        ArrayList visitadas = new ArrayList();
        String id1 = t1.getId();
        String id2 = t2.getId();
        return tiposCompatibles2(t1.getTipo(),t2.getTipo(),visitadas,id1,id2);
    }

    private boolean tiposCompatibles2(Tipos t1, Tipos t2, ArrayList visitadas, String id1, String id2) {
        if (visitadas.contains(t1) && visitadas.contains(t2)) {
            return true;
        } else {
            visitadas.add(t1);
            visitadas.add(t2);
            if ((t1.getT() == tiposPerm.BOOLEAN) && (t2.getT() == tiposPerm.BOOLEAN) ||
                    ((t1.getT() == tiposPerm.INTEGER) && (t2.getT() == tiposPerm.NATURAL || t2.getT() == tiposPerm.INTEGER))
                    || (t1.getT() == tiposPerm.NATURAL) && (t2.getT() == tiposPerm.NATURAL) ||
                    ((t1.getT() == tiposPerm.FLOAT) && (t2.getT() == tiposPerm.NATURAL || t2.getT() == tiposPerm.INTEGER || t2.getT() == tiposPerm.FLOAT))) {
                return true;
            } else {
                if (t1.getT() == tiposPerm.REF) {
                    AtributosSintact aux = (AtributosSintact) ts.get(id1);
                    return tiposCompatibles2(aux.getTipo(), t2, visitadas, id1, id2);
                } else {
                    if (t2.getT() == tiposPerm.REF) {
                        AtributosSintact aux = (AtributosSintact) ts.get(id2);
                        return tiposCompatibles2(t1, aux.getTipo(), visitadas, id1, id2);
                    } else {
                        if (t1.getT() == tiposPerm.ARRAY && t2.getT() == tiposPerm.ARRAY) {
                            Tipo_Array ta1 = (Tipo_Array) t1;
                            Tipo_Array ta2 = (Tipo_Array) t2;
                            if (ta1.getNelems() == ta2.getNelems()) {
                                return tiposCompatibles2(t1.getTbase(), t2.getTbase(), visitadas, id1, id2);
                            } else {
                                if (t1.getT() == tiposPerm.RECORD && t2.getT() == tiposPerm.RECORD) {
                                    Tipo_Record tr1 = (Tipo_Record) t1;
                                    Tipo_Record tr2 = (Tipo_Record) t2;
                                    if (tr1.getCampos().size() == tr2.getCampos().size()) {
                                        for (int i = 1; i < tr1.getCampos().size(); i++) {
                                            Campo c1 = (Campo) tr1.getCampos().get(i);
                                            Campo c2 = (Campo) tr2.getCampos().get(i);
                                            if (!tiposCompatibles2(c1.getTipo1(), c2.getTipo1(), visitadas, id1, id2)) {
                                                return false;
                                            }
                                        }
                                        return true;
                                    } else {
                                        if (t1.getT() == tiposPerm.POINTER && t2.getT() == tiposPerm.POINTER) {
                                            return tiposCompatibles2(t1.getTbase(), t2.getTbase(), visitadas, id1, id2);
                                        } else {
                                            return false;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        return false;
    }

    private AtributosSintact opNivel0(CatLexica cl) throws Exception {
        AtributosSintact op0 = new AtributosSintact();
        switch (cl) {
            case MENOR:
                op0.setCod("menor");
                op0.setBC(Instruction.code_MENOR);
                op0.setCodString("menor");
                break;
            case MAYOR:
                op0.setCod("mayor");
                op0.setBC(Instruction.code_MAYOR);
                op0.setCodString("mayor");
                break;
            case MENOR_IGUAL:
                op0.setCod("menorigual");
                op0.setBC(Instruction.code_MENIG);
                op0.setCodString("menorigual");
                break;
            case MAYOR_IGUAL:
                op0.setCod("mayorigual");
                op0.setBC(Instruction.code_MAYIG);
                op0.setCodString("mayorigual");
                break;
            case IGUAL:
                op0.setCod("igual");
                op0.setBC(Instruction.code_IGUAL);
                op0.setCodString("igual");
                break;
            case DISTINTO:
                op0.setCod("distinto");
                op0.setBC(Instruction.code_DIST);
                op0.setCodString("distinto");
                break;
        }
        return op0;
    }

    private AtributosSintact opNivel1(CatLexica cl) throws Exception {
        AtributosSintact op1 = new AtributosSintact();
        switch (cl) {
            case MAS:
                op1.setCod("suma");
                op1.setBC(Instruction.code_SUMA);
                op1.setCodString("suma");
                break;
            case MENOS:
                op1.setCod("resta");
                op1.setBC(Instruction.code_RESTA);
                op1.setCodString("resta");
                break;
            case OR:
                op1.setCod("or");
                op1.setBC(Instruction.code_OR);
                op1.setCodString("or");
                break;
        }
        return op1;
    }

    private AtributosSintact opNivel2(CatLexica cl) throws Exception {
        AtributosSintact op2 = new AtributosSintact();
        switch (cl) {
            case MULT:
                op2.setCod("multiplica");
                op2.setBC(Instruction.code_MULT);
                op2.setCodString("multiplica");
                break;
            case DIV:
                op2.setCod("divide");
                op2.setBC(Instruction.code_DIV);
                op2.setCodString("divide");
                break;
            case MOD:
                op2.setCod("modulo");
                op2.setBC(Instruction.code_MOD);
                op2.setCodString("modulo");
                break;
            case AND:
                op2.setCod("and");
                op2.setBC(Instruction.code_AND);
                op2.setCodString("and");
                break;
        }
        return op2;
    }

    private AtributosSintact opNivel3(CatLexica cl) throws Exception {
        AtributosSintact op3 = new AtributosSintact();
        if (cl == CatLexica.DESP_DER) {
            op3.setCod("despDer");
            op3.setCodString("despDer");
            op3.setBC(Instruction.code_DESPD);
        } else if (cl == CatLexica.DESP_IZQ) {
            op3.setCod("despIzq");
            op3.setCodString("despIzq");
            op3.setBC(Instruction.code_DESPI);
        }
        return op3;
    }

    private AtributosSintact opNivel4a(CatLexica cl) throws Exception {
        AtributosSintact op4a = new AtributosSintact();
        if (cl == CatLexica.NOT) {
            op4a.setCod("negacion");
            op4a.setCodString("negacion");
            op4a.setBC(Instruction.code_NEG);
        } else if (cl == CatLexica.MENOS) {
            op4a.setCod("cambiarSigno");
            op4a.setCodString("cambiarSigno");
            op4a.setBC(Instruction.code_SIGNO);
        }
        return op4a;
    }

    private AtributosSintact opNivel4b(CatLexica cl) throws Exception {
        AtributosSintact op4b = new AtributosSintact();
        switch (cl) {
            case CONV_NAT:
                op4b.setCod("castNat");
                op4b.setBC(Instruction.code_CNAT);
                op4b.getTipo().setT(tiposPerm.NATURAL);
                op4b.setCodString("castNat");
                break;
            case CONV_INT:
                op4b.setCod("castInt");
                op4b.setBC(Instruction.code_CINT);
                op4b.getTipo().setT(tiposPerm.INTEGER);
                op4b.setCodString("castInt");
                break;
            case CONV_FLOAT:
                op4b.setCod("castFloat");
                op4b.setBC(Instruction.code_CFLOAT);
                op4b.getTipo().setT(tiposPerm.FLOAT);
                op4b.setCodString("castFloat");
                break;
            case CONV_CHAR:
                op4b.setCod("castChar");
                op4b.setBC(Instruction.code_CCHAR);
                op4b.getTipo().setT(tiposPerm.CHAR);
                op4b.setCodString("castChar");
                break;
        }
        return op4b;
    }

    private tiposPerm tipoOpNivel0(tiposPerm t1, tiposPerm t2) throws Exception {
        if ((t1 == t2) && (t1 != tiposPerm.ERR)) {
            return tiposPerm.BOOLEAN;
        } else {
            if ((t1 != tiposPerm.BOOLEAN && t1 != tiposPerm.CHAR) &&
                    (t2 != tiposPerm.BOOLEAN && t2 != tiposPerm.CHAR)) {
                return tiposPerm.BOOLEAN;
            } else {
                err.add("Linea " + token.getNumLinea() + " , Columna " + token.getNumCol() + ": Tipos no compatibles");
                return tiposPerm.ERR;
            }
        }
    }

    private tiposPerm tipoOpNivel1(tiposPerm t1, tiposPerm t2, AtributosSintact o) throws Exception {
        if (o.getCod().equals("or")) {
            if ((t1 == t2) && (t1 == tiposPerm.BOOLEAN)) {
                return tiposPerm.BOOLEAN;
            } else {
                err.add("Linea " + token.getNumLinea() + " , Columna " + token.getNumCol() + ": Expresion no booleana");
                return tiposPerm.ERR;
            }
        } else {
            if (t1 == tiposPerm.ERR || t2 == tiposPerm.ERR ||
                    t1 == tiposPerm.BOOLEAN || t2 == tiposPerm.BOOLEAN ||
                    t1 == tiposPerm.CHAR || t2 == tiposPerm.CHAR) {
                err.add("Linea " + token.getNumLinea() + " , Columna " + token.getNumCol() + ": Tipos no compatibles");
                return tiposPerm.ERR;
            } else {
                if (t1 == tiposPerm.FLOAT || t2 == tiposPerm.FLOAT) {
                    return tiposPerm.FLOAT;
                } else {
                    if (t1 == tiposPerm.INTEGER || t2 == tiposPerm.INTEGER) {
                        return tiposPerm.INTEGER;
                    } else {
                        return tiposPerm.NATURAL;
                    }
                }
            }
        }
    }

    private tiposPerm tipoOpNivel2(tiposPerm t1, tiposPerm t2, AtributosSintact o) throws Exception {
        if (o.getCod().equals("and")) {
            if ((t1 == t2) && (t1 == tiposPerm.BOOLEAN)) {
                return tiposPerm.BOOLEAN;
            } else {
                err.add("Linea " + token.getNumLinea() + " , Columna " + token.getNumCol() + ": Expresion no booleana");
                return tiposPerm.ERR;
            }
        } else {
            if (o.getCod().equals("modulo")) {
                if (t2 != tiposPerm.NATURAL) {
                    err.add("Linea " + token.getNumLinea() + " , Columna " + token.getNumCol() + ": El segundo operando debe ser un natural");
                    return tiposPerm.ERR;
                } else {
                    if (t1 != tiposPerm.NATURAL && t1 != tiposPerm.INTEGER) {
                        err.add("Linea " + token.getNumLinea() + " , Columna " + token.getNumCol() + ": El primer operando debe ser entero o natural");
                        return tiposPerm.ERR;
                    } else {
                        return t1;
                    }
                }
            } else {
                if (t1 == tiposPerm.ERR || t2 == tiposPerm.ERR ||
                        t1 == tiposPerm.BOOLEAN || t2 == tiposPerm.BOOLEAN ||
                        t1 == tiposPerm.CHAR || t2 == tiposPerm.CHAR) {
                    err.add("Linea " + token.getNumLinea() + " , Columna " + token.getNumCol() + ": Tipos no compatibles");
                    return tiposPerm.ERR;
                } else {
                    if (t1 == tiposPerm.FLOAT || t2 == tiposPerm.FLOAT) {
                        return tiposPerm.FLOAT;
                    } else {
                        if (t1 == tiposPerm.INTEGER || t2 == tiposPerm.INTEGER) {
                            return tiposPerm.INTEGER;
                        } else {
                            return tiposPerm.NATURAL;
                        }
                    }
                }
            }
        }
    }

    private tiposPerm tipoOpNivel3(tiposPerm t1, tiposPerm t2) throws Exception {
        if (t1 == t2 && t1 == tiposPerm.NATURAL) {
            return t1;
        } else {
            err.add("Linea " + token.getNumLinea() + " , Columna " + token.getNumCol() + ": Los operandos deben ser naturales");
            return tiposPerm.ERR;
        }
    }

    private tiposPerm tipoOpNivel4a(tiposPerm t1, AtributosSintact as) throws Exception {
        if (as.getCod().equals("negacion")) {
            if (t1 == tiposPerm.BOOLEAN) {
                return t1;
            } else {
                err.add("Linea " + token.getNumLinea() + " , Columna " + token.getNumCol() + ": El operando debe ser booleano");
                return tiposPerm.ERR;
            }
        } else {//Es un cambio de signo
            if (t1 == tiposPerm.ERR || t1 == tiposPerm.BOOLEAN ||
                    t1 == tiposPerm.CHAR) {
                err.add("Linea " + token.getNumLinea() + " , Columna " + token.getNumCol() + ": El operando debe ser numerico");
                return tiposPerm.ERR;
            } else {
                if (t1 == tiposPerm.FLOAT) {
                    return t1;
                } else {
                    return tiposPerm.INTEGER;
                }
            }
        }
    }

    private tiposPerm tipoOpNivel4b(tiposPerm t1, AtributosSintact as) throws Exception {
        if (t1 == tiposPerm.BOOLEAN) {
            err.add("Linea " + token.getNumLinea() + " , Columna " + token.getNumCol() + ": No se admiten booleanos");
            return tiposPerm.ERR;
        } else {
            if (as.getTipo().getT() == tiposPerm.NATURAL || as.getTipo().getT() == tiposPerm.CHAR) {
                if (t1 == tiposPerm.INTEGER || t1 == tiposPerm.FLOAT) {
                    err.add("Linea " + token.getNumLinea() + " , Columna " + token.getNumCol() + ": No se admiten reales o enteros");
                    return tiposPerm.ERR;
                } else {
                    return as.getTipo().getT();
                }
            } else {
                return as.getTipo().getT();
            }
        }
    }


    /*Metodo que comprueba que el token siguiente sea el esperado y que devuelve
     * error en caaso de que no sea correcto
     */
    private void ckToken(CatLexica tk) throws Exception {
        if (token.getCat() != CatLexica.EOF) {
            if (tk == token.getCat()) {
                token = anLex.sigToken(); //avanzamos al siguiente token
            } else {
                throw new Exception("Linea " + token.getNumLinea() + " , Columna " + token.getNumCol() + ": Error de sintaxis, token '"+ token.getLex() +"' no esperado\n");
            }
        }
    }

    /*Funcion que concatena instrucciones de la maquina P para escribirlas al
    final en el archivo destino*/
    private void emite(String instruccion) {
        cod.add(etq + " " + instruccion + "\n");
    }

    private void emiteBC(int code) {
        emiteBC(code, null);
    }

    private void emiteBC(int code, String field) {
        codByte.add(new Instruction(etq, (byte) code, field));
        etq+=1;
    }

    /*Funcion que comprueba si ya existe el id en la tabla de simbolos */
    private boolean existeID(String id) {
        return ts.containsKey(id);
    }

    /*Metodo que introduce un nuevo idd en la tabla de simbolos*/
    private void ponID(String id, AtributosSintact atrib) {
        ts.put(id, atrib);
    }

    /*Funcion que comprueba si un lexema dado es una palabra reservada y no
    se podra usar*/
    private boolean esPalabraReservada(String lex) {
        String aux = lex.toUpperCase();
        if (palRes.containsKey(aux)) {
            return true;
        }
        return false;
    }

    //Metodo que devuelve el tipo del siguiente token.
    private AtributosSintact Tipo(boolean esPuntero) throws Exception {
        AtributosSintact tipo = new AtributosSintact();
        CatLexica actual = token.getCat();

        if (actual == CatLexica.INTEGER) {
            tipo.getTipo().setT(tiposPerm.INTEGER);
            ckToken(CatLexica.INTEGER);
        } else if (actual == CatLexica.NATURAL) {
            tipo.getTipo().setT(tiposPerm.NATURAL);
            ckToken(CatLexica.NATURAL);
        } else if (actual == CatLexica.FLOAT) {
            tipo.getTipo().setT(tiposPerm.FLOAT);
            ckToken(CatLexica.FLOAT);
        } else if (actual == CatLexica.BOOLEAN) {
            tipo.getTipo().setT(tiposPerm.BOOLEAN);
            ckToken(CatLexica.BOOLEAN);
        } else if (actual == CatLexica.CHAR) {
            tipo.getTipo().setT(tiposPerm.CHAR);
            ckToken(CatLexica.CHAR);
        } else if (actual == CatLexica.IDENT) {
            boolean error = true;
            tipo.getTipo().setT(tiposPerm.REF);
            String lexemaIdent = token.getLex();
            tipo.setId(lexemaIdent);
            tipo.getTipo().setIdt(lexemaIdent);
            if (existeID(lexemaIdent)) {
                AtributosSintact aux = (AtributosSintact) ts.get(lexemaIdent);
                error = aux.getClase() != clases.TIPO;
                tipo.getTipo().setTam(aux.getTipo().getTam());
            }
            //Pendientes
            else if (esPuntero && !pend.contains(lexemaIdent)) {
                pend.add(lexemaIdent);
                error = false;
            }
            //Error
            if (error) {
                err.add("Linea " + token.getNumLinea() + " , Columna " + token.getNumCol() + ": El identificador no es de la clase tipo");
            }
            ckToken(CatLexica.IDENT);
        } else if (actual == CatLexica.ARRAY) {
            ckToken(CatLexica.ARRAY);
            Tipo_Array aux = new Tipo_Array();
            ckToken(CatLexica.COR_AP);
            String natArray = token.getLex();
            int nat = Integer.valueOf(natArray);
            ckToken(CatLexica.NUM_NAT);
            ckToken(CatLexica.COR_CIE);
            ckToken(CatLexica.OF);
            AtributosSintact tipo1 = Tipo(false);
            aux.setNelems(nat);
            aux.setTam(nat * tipo1.getTipo().getTam());
            aux.setTbase(tipo1.getTipo());
            aux.setT(tiposPerm.ARRAY);
            tipo.setTipo(aux);
            if (referenciaErronea(ts, tipo1)) {
                err.add("Linea " + token.getNumLinea() + " , Columna " + token.getNumCol() + ": Tipo de los elementos del array no declarado");
            }
        } else if (actual == CatLexica.RECORD) {
            ckToken(CatLexica.RECORD);
            Tipo_Record aux = new Tipo_Record();
            aux.setT(tiposPerm.RECORD);
            AtributosSintact auxCamp = BloqueCamp();
            Tipo_Record tr = (Tipo_Record) auxCamp.getTipo();
            aux.setCampos(tr.getCampos());
            aux.setTam(auxCamp.getTipo().getTam());
            tipo.setTipo(aux);
        } else if (actual == CatLexica.POINTER) {
            ckToken(CatLexica.POINTER);
            Tipo_Pointer aux = new Tipo_Pointer();
            aux.setT(tiposPerm.POINTER);
            AtributosSintact atribAux = Tipo(true);
            aux.setTbase(atribAux.getTipo());
            tipo.setTipo(aux);
        } else {
            err.add("Linea " + token.getNumLinea() + " , Columna " + token.getNumCol() + ": Tipo no permitido");
            tipo.getTipo().setT(tiposPerm.ERR);
        }
        return tipo;
    }

    private boolean referenciaErronea(Hashtable ts, AtributosSintact t1) throws Exception {
        return t1.getTipo().getT() == tiposPerm.REF && !existeID(t1.getId());
    }

    private AtributosSintact BloqueCamp() throws Exception {
        ckToken(CatLexica.LL_AP);
        AtributosSintact atr = Campos();
        ckToken(CatLexica.LL_CIE);
        return atr;
    }

    private AtributosSintact Campos() throws Exception {
        AtributosSintact campo = Campo(0);
        Tipo_Record tr = (Tipo_Record) campo.getTipo();
        tr.setDesplaz(campo.getTipo().getTam());
        return RC(tr.getDesplaz(), tr.getCampos());
    }

    private AtributosSintact RC(int desh, ArrayList camposh) throws Exception {
        AtributosSintact rc0 = new AtributosSintact();
        Tipo_Record trrc0 = new Tipo_Record();
        if (token.getCat() == CatLexica.PYCOMA) {
            trrc0.setCampos(camposh);
            trrc0.setDesplaz(desh);
            ckToken(CatLexica.PYCOMA);
            AtributosSintact campo = Campo(desh);
            Tipo_Record trcampo = (Tipo_Record) campo.getTipo();
            int desh1 = trcampo.getDesplaz() + trcampo.getTam();
            AtributosSintact rc1 = RC(desh1, trcampo.getCampos());
            Tipo_Record trrc1 = (Tipo_Record) rc1.getTipo();
            trrc0.setTam(campo.getTipo().getTam() + rc1.getTipo().getTam());
            trrc0.setDesplaz(trcampo.getDesplaz() + trrc1.getDesplaz());
            int tope = trcampo.getCampos().size();
            for (int i = 0; i < tope; i++) {
                trrc0.getCampos().add(trcampo.getCampos().get(i));
            }
            //Error
            if (esCampo(camposh, campo.getId())) {
                err.add("Linea " + token.getNumLinea() + " , Columna " + token.getNumCol() + ": Campo no definido");
            }
            rc0.setTipo(trrc0);
            return rc0;
        }
        else {
            rc0.setTipo(trrc0);
            return rc0;
        }
    }

    private boolean esCampo(ArrayList campos, String id) throws Exception {
        boolean encontrado = false;
        for (int i = 0; i < campos.size(); i++) {
            Campo campo = (Campo)campos.get(i);
            if (id.equals(campo.getId())) {
                encontrado = true;
            }
        }
        return encontrado;
    }

    private AtributosSintact Campo(int desh) throws Exception {
        AtributosSintact atr = new AtributosSintact();
        AtributosSintact tipo = Tipo(false);
        String idlex = token.getLex();
        ckToken(CatLexica.IDENT);
        Tipo_Record tr = new Tipo_Record();
        Campo campo = new Campo();
        campo.setId(idlex);
        campo.setTipo1(tipo.getTipo());
        campo.setDesp(desh);
        ArrayList listaCampo = new ArrayList();
        listaCampo.add(campo);
        tr.setCampos(listaCampo);
        atr.setTipo(tr);
        atr.getTipo().setTam(tipo.getTipo().getTam());
        if (referenciaErronea(ts, tipo)) {
            err.add("Linea " + token.getNumLinea() + " , Columna " + token.getNumCol() + ": Tipo del campo no declarado");
        }
        return atr;
    }

    private AtributosSintact INew() throws Exception {
        ckToken(CatLexica.NEW);
        AtributosSintact mem = Mem();
        if (mem.getTipo().getT() != tiposPerm.POINTER) {
            err.add("Linea " + token.getNumLinea() + " , Columna " + token.getNumCol() + ": Tipo no es puntero");
        }
        if (mem.getTipo().getT() == tiposPerm.REF) {
            AtributosSintact aux = (AtributosSintact) ts.get(mem.getId());
            emite("new(" + aux.getTipo().getTam() + ")"); //ts[tipo1.id].tam
            emiteBC(Instruction.code_NEW, Integer.toString(mem.getTipo().getTam()));
        } else {
            emite("new(" + mem.getTipo().getTbase().getTam() + ")"); //tipo1.tbase.tam
            emiteBC(Instruction.code_NEW, Integer.toString(mem.getTipo().getTbase().getTam()));
        }
        emite("desapila_ind");
        emiteBC(Instruction.code_DESAPILA_IND);
        return mem;
    }

    private AtributosSintact IDispose() throws Exception {
        ckToken(CatLexica.DISPOSE);
        AtributosSintact mem = Mem();
        if (mem.getTipo().getT() != tiposPerm.POINTER) {
            err.add("Linea " + token.getNumLinea() + " , Columna " + token.getNumCol() + ": Tipo no es puntero");
        }
        if (mem.getTipo().getT() == tiposPerm.REF) {
            AtributosSintact aux = (AtributosSintact) ts.get(mem.getId());
            emite("dispose(" + aux.getTipo().getTam() + ")"); //ts[tipo1.id].tam
            emiteBC(Instruction.code_DISPOSE, Integer.toString(mem.getTipo().getTam()));
        } else {
            emite("dispose(" + mem.getTipo().getTbase().getTam() + ")"); //tipo1.tbase.tam
            emiteBC(Instruction.code_DISPOSE, Integer.toString(mem.getTipo().getTbase().getTam()));
        }
        return mem;
    }

    private AtributosSintact IProc() throws Exception {
        String idlex = token.getLex();
        AtributosSintact proc = (AtributosSintact) ts.get(idlex);
        ckToken(CatLexica.IDENT);
        if (!existeID(idlex) || (proc.getClase()!=clases.PROC)) {
            err.add("Linea " + token.getNumLinea() + " , Columna " + token.getNumCol() + ": No existe el identificador o no es de clase Procedimiento");
        }

        emite("apila_dir (0)");
        emiteBC(Instruction.code_APILA_DIR,"0");
        emite("apila (1)");
        emiteBC(Instruction.code_APILA,"1");
        emite("suma");
        emiteBC(Instruction.code_SUMA);
        int marca = cod.size();
        emite("apila (#)");
        emiteBC(Instruction.code_APILA);  //debe parchearse
        emite("desapila_ind");
        emiteBC(Instruction.code_DESAPILA_IND);

        AParams(proc);
        emite("ir_a(" + proc.getInicio() + ")");
        emiteBC(Instruction.code_IR_A, Integer.toString(proc.getInicio()));
        parcheaIProc(marca, etq);

        return new AtributosSintact();
    }

    private Tipos referenciada(Tipos t1) {
    //Sirve para seguir una cadena de alias y llegar al tipo básico (nums, bool...)
        if (t1.getT() == tiposPerm.REF) {
            if (existeID(t1.getIdt())) {
            //devuelve referenciada(ts[t1.id].tipo,ts)
                String id1 = t1.getIdt();
                AtributosSintact aux = (AtributosSintact) ts.get(id1);
                return referenciada(aux.getTipo());
            }
            else {
                t1.setT(tiposPerm.ERR);
                return t1;
            }
        }
        else return t1;
    }

    private AtributosSintact Mem() throws Exception {
        String idlex = token.getLex();
        ckToken(CatLexica.IDENT);
        Tipos tipoh = new Tipos();
        AtributosSintact aux = new AtributosSintact();
        if (existeID(idlex)) {
            aux = (AtributosSintact) ts.get(idlex);
            if (aux.getClase() == clases.VAR || aux.getClase() == clases.PVAR) {
                tipoh = referenciada(aux.getTipo());
                aux.setTipo(tipoh);
            } else {
                tipoh.setT(tiposPerm.ERR);
                err.add("Linea " + token.getNumLinea() + " , Columna " + token.getNumCol() + ": No existe el identificador");
                aux.setTipo(tipoh);
            }
        } else {
            tipoh.setT(tiposPerm.ERR);
            err.add("Linea " + token.getNumLinea() + " , Columna " + token.getNumCol() + ": No existe el identificador");
            aux.setTipo(tipoh);
        }
        accesoVar(aux);
        return RM(aux);
    }

    private AtributosSintact RM(AtributosSintact tipoh) throws Exception {
        AtributosSintact rm0 = tipoh;
        CatLexica actual = token.getCat();
        switch (actual) {
            case FLECHA:
                ckToken(CatLexica.FLECHA);
                if (rm0.getTipo().getT() == tiposPerm.POINTER) {
                    rm0.setTipo(referenciada(tipoh.getTipo().getTbase()));
                } else {
                    rm0.getTipo().setT(tiposPerm.ERR);
                    err.add("Linea " + token.getNumLinea() + " , Columna " + token.getNumCol() + ": El tipo no es puntero");
                }
                emite("apila_ind");
                emiteBC(Instruction.code_APILA_IND);
                return RM(rm0);

            case COR_AP:
                ckToken(CatLexica.COR_AP);
                AtributosSintact exp0 = Exp0(false,false);
                ckToken(CatLexica.COR_CIE);
                if (rm0.getTipo().getT() == tiposPerm.ARRAY || exp0.getTipo().getT() == tiposPerm.NATURAL) {
                    rm0.setTipo(referenciada(tipoh.getTipo().getTbase()));
                } else {
                    rm0.getTipo().setT(tiposPerm.ERR);
                    err.add("Linea " + token.getNumLinea() + " , Columna " + token.getNumCol() + ": No es de tipo ARRAY o NATURAL");
                }
                emite("apila(" + rm0.getTipo().getTam() + ")");
                emiteBC(Instruction.code_APILA, Integer.toString(rm0.getTipo().getTam()));
                emite("multiplica");
                emiteBC(Instruction.code_MULT);
                emite("suma");
                emiteBC(Instruction.code_SUMA);
                return RM(rm0);

            case PUNTO:
                ckToken(CatLexica.PUNTO);
                String idlex = token.getLex();
                ckToken(CatLexica.IDENT);
                Campo campo = new Campo();
                if (rm0.getTipo().getT() == tiposPerm.RECORD) {
                    Tipo_Record tr = (Tipo_Record) tipoh.getTipo();
                    if (esCampo(tr.getCampos(), idlex)) {
                        ArrayList campos = tr.getCampos();
                        int indice = 0;
                        for (int i = 0; i < campos.size(); i++) {
                            Campo c = (Campo) campos.get(i);
                            if (idlex.equals(c.getId())) {
                                indice = i;
                            }
                        }
                        campo = (Campo) campos.get(indice);
                        rm0.setTipo(referenciada(campo.getTipo1()));
                    } else {
                        rm0.getTipo().setT(tiposPerm.ERR);
                        err.add("Linea " + token.getNumLinea() + " , Columna " + token.getNumCol() + ": No existe el campo");
                    }
                } else {
                    rm0.getTipo().setT(tiposPerm.ERR);
                    err.add("Linea " + token.getNumLinea() + " , Columna " + token.getNumCol() + ": No es de tipo RECORD");
                }
                emite("apila(" + campo.getDesp() + ")");
                emiteBC(Instruction.code_APILA);
                emite("suma");
                emiteBC(Instruction.code_SUMA);
                return RM(rm0);

            default:
                return rm0;
        }
    }

    /*Metodo que rellena una tabla hash con las palabras reservadas de modo
    que sea sencillo comprobar si un lexema es palabra reservada*/
    private void rellenarTablaPalRes() {
        palRes = new Hashtable();
        palRes.put("NATURAL", 0);
        palRes.put("INTEGER", 0);
        palRes.put("FLOAT", 0);
        palRes.put("BOOLEAN", 0);
        palRes.put("TRUE", 0);
        palRes.put("FALSE", 0);
        palRes.put("CHAR", 0);
        palRes.put("AND", 0);
        palRes.put("OR", 0);
        palRes.put("NOT", 0);
        palRes.put("IN", 0);
        palRes.put("OUT", 0);
        palRes.put("&", 0);
        palRes.put("IF", 0);
        palRes.put("THEN", 0);
        palRes.put("ELSE", 0);
        palRes.put("WHILE", 0);
        palRes.put("FOR", 0);
        palRes.put("PROC", 0);
    }

    public ArrayList<String> getErr() {
        return err;
    }

    public void generarArchivo() {

        try {

            for (int i = 0; i < cod.size(); i++) {
                archivoCodigo.print((String) cod.get(i)); //al final del todo llenamos el archivo de codigo
            }
            for (int i = 0; i < codByte.size(); i++) {
                archivoCodigoBytecode.writeObject(codByte.get(i));
            }

        } catch (IOException ex) {
            ex.printStackTrace();
        }

    }

    public ArrayList getPend() {
        return pend;
    }

    public void setPend(ArrayList pend) {
        this.pend = pend;
    }

    private void accesoVar(AtributosSintact id) {
        int dirDisplay = 1+ id.getN();
        emite("apila_dir ("+(dirDisplay)+")");
        emiteBC(Instruction.code_APILA_DIR,Integer.toString(dirDisplay));
        emite("apila ("+(id.getDir())+")");
        emiteBC(Instruction.code_APILA,Integer.toString(id.getDir()));
        emite("suma");
        emiteBC(Instruction.code_SUMA);
        if (id.getClase()==clases.PVAR){
            emite("apila_ind");
            emiteBC(Instruction.code_APILA_IND);
        }
    }
}